package ru.amse.tsyganov.jumleditor.storing;

import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ru.amse.tsyganov.jumleditor.model.ActivityGraph;
import ru.amse.tsyganov.jumleditor.model.ModelElement;
import ru.amse.tsyganov.jumleditor.model.State;
import ru.amse.tsyganov.jumleditor.model.Transition;
import ru.amse.tsyganov.jumleditor.view.ViewVisitor;
import ru.amse.tsyganov.jumleditor.view.representation.ActivityGraphView;
import ru.amse.tsyganov.jumleditor.view.representation.StateView;
import ru.amse.tsyganov.jumleditor.view.representation.TransitionView;

public class XmlWriteVisitor extends ViewVisitor {
	
	private boolean visitTransitions;
	
	private final Document documentRoot;
	
	private final Element modelRoot;
	
	private final Element viewRoot;
	
	private final Map<ModelElement, Integer> modelToIdMap;
	
	private Element currentActivityGraphElement;
	
	private Element currentActivityGraphViewElement;
	
	private int currentId;
	
	public XmlWriteVisitor(Document root, boolean visitTransitions) {
		
		this.documentRoot = root;
		
		this.visitTransitions = visitTransitions;
		
		Element activityUmlGraphElement = documentRoot.
			createElement(TagNames.ACTIVITY_UML_CHART);
		
		documentRoot.appendChild(activityUmlGraphElement);
		
		modelRoot = documentRoot.createElement(TagNames.MODEL);
		viewRoot = documentRoot.createElement(TagNames.VIEW);
		
		activityUmlGraphElement.appendChild(modelRoot);
		activityUmlGraphElement.appendChild(viewRoot);
		
		modelToIdMap = new HashMap<ModelElement, Integer>();
	}

	@Override
	public void visitActivityGraphView(
			final ActivityGraphView<? extends ActivityGraph> g) {
		
		if (modelToIdMap.containsKey(g.getModel())) {
			return;
		}
		
		int id = currentId++;
		modelToIdMap.put(g.getModel(), id);
		
		currentActivityGraphElement = createNewElement(
				TagNames.ACTIVITY_GRAPH_MODEL, id, g.getModel());
		
		modelRoot.appendChild(currentActivityGraphElement);
		
		currentActivityGraphViewElement = createNewElement(
				TagNames.ACTIVITY_GRAPH_VIEW, id, new XmlStorable() {

					public String getClassName() {
						return g.getClassName();
					}

					public void writeToXml(Element element) {
					}
					
				});
		
		viewRoot.appendChild(currentActivityGraphViewElement);
	}

	@Override
	public void visitStateView(final StateView<? extends State> s) {
		
		if (modelToIdMap.containsKey(s.getModel())) {
			return;
		}
		
		int id = currentId++;
		modelToIdMap.put(s.getModel(), id);
		
		assert currentActivityGraphElement != null;
		assert currentActivityGraphViewElement != null;
		
		Element stateElement = createNewElement(
				TagNames.STATE_MODEL, id, s.getModel());
		
		currentActivityGraphElement.appendChild(stateElement);
		
		Element stateViewElement = createNewElement(
				TagNames.STATE_VIEW, id, 
				new XmlStorable() {

					public String getClassName() {
						return s.getClassName();
					}

					public void writeToXml(Element element) {
						element.setAttribute(TagNames.STATE_VIEW_X, Integer.toString(s.getX()));
						element.setAttribute(TagNames.STATE_VIEW_Y, Integer.toString(s.getY()));
						element.setAttribute(TagNames.STATE_VIEW_WIDTH, Integer.toString(s.getWidth()));
						element.setAttribute(TagNames.STATE_VIEW_HEIGHT, Integer.toString(s.getHeight()));
					}
					
				});
		
		currentActivityGraphViewElement.appendChild(stateViewElement);
	}

	@Override
	public void visitTransitionView(final TransitionView<? extends Transition> t) {
		if (!visitTransitions || modelToIdMap.containsKey(t.getModel())) {
			return;
		}
		
		int id = currentId++;
		modelToIdMap.put(t.getModel(), id);
		
		assert currentActivityGraphElement != null;
		assert currentActivityGraphViewElement != null;
		
		Element transitionElement = createNewElement(
				TagNames.TRANSITION_MODEL, id, t.getModel());
		
		currentActivityGraphElement.appendChild(transitionElement);
		
		State source = t.getModel().getSource().getContainer();
		State target = t.getModel().getTarget().getContainer();
		
		assert source != null;
		assert target != null;
		assert modelToIdMap.containsKey(source);
		assert modelToIdMap.containsKey(target);
		
		transitionElement.setAttribute(TagNames.TRANSITION_SOURCE_ID, 
				modelToIdMap.get(source).toString());
		
		transitionElement.setAttribute(TagNames.TRANSITION_TARGET_ID, 
				modelToIdMap.get(target).toString());
			
		Element transitionViewElement = createNewElement(
				TagNames.TRANSITION_VIEW, id, 
				new XmlStorable() {

					public String getClassName() {
						return t.getClassName();
					}

					public void writeToXml(Element element) {
						
					}
					
				});
		
		transitionViewElement.setAttribute(TagNames.TRANSITION_SOURCE_CONN_POINT, 
				t.getSourceConnectionPoint().getNumber().toString());
		
		transitionViewElement.setAttribute(TagNames.TRANSITION_TARGET_CONN_POINT, 
				t.getTargetConnectionPoint().getNumber().toString());
		
		transitionViewElement.setAttribute(TagNames.VERTICAL_SEGMENT, 
				String.valueOf(t.getBrokenLineVerticalSegment()));
		
		transitionViewElement.setAttribute(TagNames.HORIZONTAL_SEGMENT, 
				String.valueOf(t.getBrokenLineHorizontalSegment()));
		
		currentActivityGraphViewElement.appendChild(transitionViewElement);
	}

	private final Element createNewElement(String name, int id, 
			XmlStorable state) {
		
		Element newElement = documentRoot.createElement(name);
		newElement.setAttribute(TagNames.ID, Integer.toString(id));
		
		Element stateElement = documentRoot.createElement(
				TagNames.ENTITY_STATE);
		
		newElement.appendChild(stateElement);
		
		stateElement.setAttribute(TagNames.CLASS_NAME, 
					state.getClassName());
		
		state.writeToXml(stateElement);
		
		return newElement;
	}

	public boolean isVisitTransitions() {
		return visitTransitions;
	}

	public void setVisitTransitions(boolean visitTransitions) {
		this.visitTransitions = visitTransitions;
	}
}
